/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ai;

import java.sql.Connection;
import java.util.*;

/**
 *
 * @author mammad
 */
public class QueryForm {

    public static final int INC = 0;
    public static final int EXC = 1;
    private LinkedList<String> mIncIngredients;
    private LinkedList<String> mExcIngredients;
    private LinkedList<String> mMealCategory;
    private LinkedList<String> mMeal;
    private LinkedList<String> mfinalMeals;
    private LinkedList<String> databaseIngr;
    private LinkedList<String> databaseMealCat;
    private Connection mConn;
    private int nrOfPeople;
    private int mLastEntry = -1;
    static final int OBJTYPE_INGREDIENT = 0;
    static final int OBJTYPE_MEALCAT = 1;

    private class Obj {

        public Obj(int pCount, int pType, String pName) {
            mCount = pCount;
            mType = pType;
            mName = pName;
        }
        public int mCount;
        public int mType;
        public String mName;
    }

    public QueryForm(Connection pConn) {
        mConn = pConn;
        mIncIngredients = new LinkedList<String>();
        mExcIngredients = new LinkedList<String>();
        mMealCategory = new LinkedList<String>();
        mfinalMeals = new LinkedList<String>();
        mMeal = new LinkedList<String>();
        databaseIngr = new LinkedList<String>();
        databaseMealCat = new LinkedList<String>();
        nrOfPeople = 4;
    //  mExcIngredients.add("flour");
    // mExcIngredients.add("egg");
    // mIncIngredients.add("butter");

    }

    public int getLastEntry() {
        return mLastEntry;
    }

    public LinkedList<String> getIncIngrediens() {
        return mIncIngredients;

    }

    public LinkedList<String> getExcIngrediens() {
        return mExcIngredients;

    }

    public LinkedList<String> getMealCategory() {
        return mMealCategory;

    }

    public LinkedList<String> getFinalMeals() {
        return mfinalMeals;

    }

    public LinkedList<String> getMeal() {
        return mMeal;

    }

    public int getNrOfPeople() {
        return nrOfPeople;

    }

    public void addIncIngredient(String pIngredient) {
        if (mExcIngredients.contains(pIngredient)) {
            mExcIngredients.remove(pIngredient);
        }
        if (!mIncIngredients.contains(pIngredient)) {
            mIncIngredients.add(pIngredient);
        }
    }

    public void addExcIngredient(String pIngredient) {
        if (mIncIngredients.contains(pIngredient)) {
            mIncIngredients.remove(pIngredient);
        }
        if (!mExcIngredients.contains(pIngredient)) {
            mExcIngredients.add(pIngredient);
        }
    }

    public void addMealCategory(String mealCategory) {
        mMealCategory.add(mealCategory);
    }

    public void removeMealCategory(String mealCategory) {
        mMealCategory.remove(mealCategory);
    }

    public void addMeal(String meal) {
        mMealCategory.add(meal);
    }

    public void addFinalMeals(String meal) {
        mfinalMeals.add(meal);
    }

    public void setNrOfPeople(int nrToCoockFor) {
        nrOfPeople = nrToCoockFor;

    }

    public void addDatabaseIngr(String ingr) {
        databaseIngr.add(ingr);
    }

    public LinkedList<String> getDatabaseIngr() {
        return databaseIngr;
    }
    public void addDatabaseMealcat(String mealCat) {
        databaseMealCat.add(mealCat);
    }

    public LinkedList<String> getDatabaseMealCat() {
        return databaseMealCat;
    }

    public void execute(SemanticExp pSE) throws ParseException {
        if (pSE.getType() != SemanticExp.FUNC) {
            throw new ParseException("Semantic expression " + pSE + " is not a function.");
        }
        if (pSE.getName().equals("inc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeInc(s);
            }

        } else if (pSE.getName().equals("exc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeExc(s);
            }
        } else if (pSE.getName().equals("nop")) {
        } else if (pSE.getName().equals("not")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeNot(s);
            }
        } else if(pSE.getName().equals("and")){
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for(SemanticExp s : lList){
                execute(s);
            }
        }
        else{
            throw new ParseException("Semantic expression "+ pSE + " is neither \"inc\" nor \"exc\".");
        }
    }

    public void executeNot(SemanticExp pSE) throws ParseException {
        if (pSE.getType() != SemanticExp.FUNC) {
            throw new ParseException("Semantic expression " + pSE + " is not a function.");
        }
        if (pSE.getName().equals("not")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                execute(s);
            }
        } else if (pSE.getName().equals("inc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeExc(s);
            }
        } else if (pSE.getName().equals("exc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeInc(s);
            }
        } 
        else if(pSE.getName().equals("nop")){
        }
        else{
            throw new ParseException("Semantic expression "+ pSE + " is not a not, inc or exc.");
        }
    }

    public void executeInc(SemanticExp pSE) throws ParseException {
        if (pSE.getType() != SemanticExp.FUNC) {
            throw new ParseException("Semantic expression " + pSE + " is not a function.");
        }
        if (pSE.getName().equals("obj")) {
            Obj lObj = executeObj(pSE);
            // TODO: Take Obj.mCount into account...
            if (lObj != null) {
                switch (lObj.mType) {
                    case OBJTYPE_INGREDIENT:
                         {
                            addIncIngredient(lObj.mName);
                            mLastEntry = QueryForm.INC;
                            Jdbc j = new Jdbc(this);
                            j.checkAndIncIngrediens(mConn);
                        }
                        break;
                    case OBJTYPE_MEALCAT:
                         {
                            addMealCategory(lObj.mName);
                        }
                        break;
                    default: {
                        throw new ParseException("Impossible exception! If you encounter this, it's time to get religious!");
                    }
                }
            }
        } else if (pSE.getName().equals("and") || pSE.getName().equals("inc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeInc(s);
            }
        }
        else if(pSE.getName().equals("exc")){
            // inc(exc()) - allow this... Interpret as "exc".
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for(SemanticExp s : lList){
                executeExc(s);
            }
        }
        else if(pSE.getName().equals("nop")){
        }
        else if(pSE.getName().equals("or")){
            System.out.println("Please don't give me any options. I'm too stupid for that.");
        } else {
            throw new ParseException("Semantic expression " + pSE + " is not \"obj\".");
        }
    }

    public void executeExc(SemanticExp pSE) throws ParseException {
        if (pSE.getType() != SemanticExp.FUNC) {
            throw new ParseException("Semantic expression " + pSE + " is not a function.");
        }
        if (pSE.getName().equals("obj")) {
            Obj lObj = executeObj(pSE);

            if (lObj != null) {
                // TODO: Take Obj.mCount into account...
                switch (lObj.mType) {
                    case OBJTYPE_INGREDIENT:
                         {
                            addExcIngredient(lObj.mName);
                            mLastEntry = QueryForm.EXC;
                            Jdbc j = new Jdbc(this);
                            j.checkAndExcIngrediens(mConn);
                        }
                        break;
                    case OBJTYPE_MEALCAT:
                         {
                            removeMealCategory(lObj.mName);
                        }
                        break;
                    default: {
                        throw new ParseException("Impossible exception! If you encounter this, it's time to get religious!");
                    }
                }
            }
        } else if (pSE.getName().equals("and") || pSE.getName().equals("exc")) {
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for (SemanticExp s : lList) {
                executeExc(s);
            }
        }
        else if(pSE.getName().equals("inc")){
            // exc(inc()) - allow this... Interpret as "inc".
            LinkedList<SemanticExp> lList = pSE.getOutputs();
            for(SemanticExp s : lList){
                executeInc(s);
            }
        }
        else if(pSE.getName().equals("or")){

            System.out.println("Wow! That's far too complicated for me! Please keep things simple.");
        } else if (pSE.getName().equals("nop")) {
        } else {
            throw new ParseException("Semantic expression " + pSE + " is not \"obj\".");
        }
    }

    Obj executeObj(SemanticExp pSE) throws ParseException {
        LinkedList<SemanticExp> lArgs = pSE.getOutputs();
        if (lArgs.size() != 3) {
            throw new ParseException("Semantic expression " + pSE + " has wrong number of arguments.");
        }
        SemanticExp lFirst = lArgs.pollFirst();
        SemanticExp lSecond = lArgs.pollFirst();
        SemanticExp lThird = lArgs.pollFirst();

        if (lFirst.getType() != SemanticExp.CONST) {
            throw new ParseException("First argument in semantic expression " + pSE + " is not a constant.");
        }

        int lCount = 0;
        try {
            lCount = Integer.parseInt(lFirst.getName());
        } catch (NumberFormatException e) {
            throw new ParseException("First argument in semantic expression " + pSE + " is not an integer.");
        }

        if (lSecond.getType() != SemanticExp.FUNC) {
            throw new ParseException("Second argument in semantic expression " + pSE + " is not a function.");
        }
        int lObjType = -1;
        if (lSecond.getName().equals("ingr")) {
            lObjType = OBJTYPE_INGREDIENT;
        } else if (lSecond.getName().equals("mealcat")) {
            lObjType = OBJTYPE_MEALCAT;
        } else if (lSecond.getName().equals("ignore")) {
            return null;
        } else {
            throw new ParseException("Second argument in semantic expression " + pSE + " does not resolve to an object type.");
        }

        if (lThird.getType() != SemanticExp.CONST) {
            throw new ParseException("Third argument in semantic expression " + pSE + " is not a constant.");
        }

        return new Obj(lCount, lObjType, lThird.getName());
    }

    public String toString() {
        return mIncIngredients.toString();
    }
}
